import re

from playwright.sync_api import sync_playwright

"""
常见的一些 API 如点击 click，输入 fill 等操作，这些方法都是属于 Page 对象的，所以所有的方法都从 Page 对象的 API 文档查找就好了，文档地址：https://playwright.dev/python/docs/api/class-page。
"""

"""
 示例1 事件监听
 Page 对象提供了一个 on 方法，它可以用来监听页面中发生的各个事件，比如 close、console、load、request、response 等等。
 比如这里我们可以监听 response 事件，response 事件可以在每次网络请求得到响应的时候触发，我们可以设置对应的回调方法获取到对应 Response 的全部信息 
"""

# 定义监听的回调方法
# def on_response(response):
#     # print(f'Statue {response.status}: {response.url}')
#     # 即使这个Ajax请求有加密参数，我们也不用关心，因为我们直接截获了Ajax最后响应的结果
#     if '/api/movie' in response.url and response.status == 200:
#         print(response.json())
#
# with sync_playwright() as p:
#     browser = p.chromium.launch(headless=False)
#     page = browser.new_page()
#     # 监听响应事件，指定回调函数,每次页面收到响应都会执行
#     # 包括HTML、CSS、JS 等所有资源
#     page.on('response', on_response)
#     page.goto('https://spa6.scrape.center/')
#     # 等待“网络空闲”（即网络连接数小于或等于0，常用于SPA页面），确保页面及其动态内容都加载完成
#     page.wait_for_load_state('networkidle')
#     browser.close()
#

"""
 示例2 获取页面源码
 要获取页面的 HTML 代码其实很简单，我们直接通过 content 方法获取即可 
"""

# with sync_playwright() as p:
#     browser = p.chromium.launch(headless=False)
#     page = browser.new_page()
#     page.goto('https://spa6.scrape.center/')
#     page.wait_for_load_state('networkidle')
#     html = page.content()
#     print(html)
#     browser.close()


"""
 page对象其他的一些常规操作
 page.click(selector, **kwargs)  页面点击，第二个参数 可以通过 timeout 参数指定对应节点的最长等待时间
 page.fill(selector, value, **kwargs)  文本输入，第二个参数 可以通过 timeout 参数指定对应节点的最长等待时间
 page.get_attribute(selector, name, **kwargs) 获取节点属性，第二个参数 可以通过 timeout 参数指定对应节点的最长等待时间

"""

"""
  示例 3 获取节点属性
"""
# with sync_playwright() as p:
#     browser = p.chromium.launch(headless=False)
#     page = browser.new_page()
#     page.goto('https://spa6.scrape.center/')
#     page.wait_for_load_state('networkidle')
#     # 这里我们调用了 get_attribute 方法，传入的 selector 是 a.name，选定了 class 为 name 的 a 节点，然后第二个参数传入了 href，获取超链接的内容
#     href = page.get_attribute('a.name', 'href')
#     print(href)
#     browser.close()


"""
  示例4 获取多个节点
  获取所有节点可以使用 query_selector_all 方法，它可以返回节点列表，通过遍历获取到单个节点之后，我们可以接着调用单个节点的方法来进行一些操作和属性获取
  
  这里我们通过 query_selector_all 方法获取了所有匹配到的节点，每个节点对应的是一个 ElementHandle 对象，然后 ElementHandle 对象也有 get_attribute 方法来获取节点属性，另外还可以通过 text_content 方法获取节点文本。 
"""

# with sync_playwright() as p:
#     browser = p.chromium.launch(headless=False)
#     page = browser.new_page()
#     page.goto('https://spa6.scrape.center/')
#     page.wait_for_load_state('networkidle')
#     elements = page.query_selector_all('a.name')
#     for element in elements:
#         print(element.get_attribute('href'))
#         # 可以通过 text_content 方法获取节点文本
#         print(element.text_content())
#     browser.close()


"""
 示例5 获取单个节点
 获取单个节点也有特定的方法，就是 query_selector，如果传入的选择器匹配到多个节点，那它只会返回第一个节点
"""

# with sync_playwright() as p:
#     browser = p.chromium.launch(headless=False)
#     page = browser.new_page()
#     page.goto('https://spa6.scrape.center/')
#     page.wait_for_load_state('networkidle')
#     element = page.query_selector('a.name')
#     print(element.get_attribute('href'))
#     print(element.text_content())
#     browser.close()

"""
  示例6 网络劫持-取消请求
  利用 route 方法，我们可以实现一些网络劫持和修改操作，比如修改 request 的属性，修改 response 响应结果
"""

# 这里我们调用了 route 方法，第一个参数通过正则表达式传入了匹配的 URL 路径，这里代表的是任何包含 .png 或 .jpg 的链接，遇到这样的请求，会回调 cancel_request 方法处理，
# cancel_request 方法可以接收两个参数，一个是 route，代表一个 CallableRoute 对象，另外一个是 request，代表 Request 对象。这里我们直接调用了 route 的 abort 方法，取消了这次请求，所以最终导致的结果就是图片的加载全部取消了。
# with sync_playwright() as p:
#     browser = p.chromium.launch(headless=False)
#     page = browser.new_page()
#
#     def cancel_request(route, request):
#         # 取消请求
#         route.abort()
#
#     #这里我们调用了 route 方法，第一个参数通过正则表达式传入了匹配的 URL 路径，这里代表的是任何包含 .png 或 .jpg 的链接
#     page.route(re.compile(r"(\.png)|(\.jpg)"), cancel_request)
#     page.goto("https://spa6.scrape.center/")
#     page.wait_for_load_state('networkidle')
#     page.screenshot(path='no_picture.png')
#     browser.close()


"""
 示例7 网络劫持-修改响应内容
 直接修改 Response 的结果为自定义的文本文件
 可以看到，Response 的运行结果就被我们修改了，URL 还是不变的，但是结果已经成了我们修改的 HTML 代码。
 所以通过 route 方法，我们可以灵活地控制请求和响应的内容，从而在某些场景下达成某些目的
"""
with sync_playwright() as p:
    browser = p.chromium.launch(headless=False)
    page = browser.new_page()

    def modify_response(route, request):
        route.fulfill(path="./custom_response.html")
    #指定任何请求路径 都会调用 modify_response
    page.route('/', modify_response)
    page.goto("https://spa6.scrape.center/")
    browser.close()
